import { BitMath } from "../math/math";

export namespace Render_Texture
{

    export enum AutoMatrixMode
    {
        Close,
        PixelFit_WithScale,
        FixHeight,
    }
    export interface ITexture
    {
        readonly width: number;
        readonly height: number;
        readonly texture: WebGLTexture | null;
    }
    export interface ITarget extends ITexture
    {
        //readonly width: number;
        //readonly height: number;
        worldMatrix: Float32Array;
        Begin(webgl: WebGLRenderingContext): void;
        End(webgl: WebGLRenderingContext): void;
    }
    export class CanvasScreen
    {
        canvas: HTMLCanvasElement | OffscreenCanvas;
        constructor(canvas: HTMLCanvasElement | OffscreenCanvas)
        {
            this.canvas = canvas;
            this.clearcolor = new BitMath.Color();
            this.clearcolor.r = 0.5;
            this.clearcolor.b = 0.5;
            this.cleardepth = 0;

            this.autoMatrixMode = AutoMatrixMode.PixelFit_WithScale;
            this.autoMatrixValue = 1.0;

            this.SetWorldMatrix_PixelFit(this.autoMatrixValue);
        }
        get width(): number
        {
            return this.canvas.width;
        }
        get height(): number
        {
            return this.canvas.height;
        }
        get texture(): WebGLTexture | null
        {
            return null;
        }
        worldMatrix: Float32Array = new Float32Array(16);
        autoMatrixMode: AutoMatrixMode;
        autoMatrixValue: number;
        clearcolor: BitMath.Color;
        cleardepth: number;
        OnResize(): void
        {



            if (this.autoMatrixMode == AutoMatrixMode.PixelFit_WithScale)
            {
                this.SetWorldMatrix_PixelFit(this.autoMatrixValue);
            }
            else if (this.autoMatrixMode == AutoMatrixMode.FixHeight)
            {
                this.SetWorldMatrix_FixHeight(this.autoMatrixValue);
            }

        }
        SetWorldMatrix_PixelFit(pixelUnit: number = 1.0): void
        {
            let c = this.canvas;
            //去掉asp的影响
            //翻转为左上角0，0
            this.worldMatrix = new Float32Array([
                1.0 * 2 / c.width * pixelUnit, 0, 0, 0,
                0, 1 * -1 * 2 / c.height * pixelUnit, 0, 0,
                0, 0, 1, 0,
                -1, 1, 0, 1]);
        }
        SetWorldMatrix_FixHeight(height: number): void
        {
            let c = this.canvas;
            let asp = c.width / c.height;
            let width = Math.round(height * asp);
            //去掉asp的影响
            //翻转为左上角0，0
            this.worldMatrix = new Float32Array([
                1.0 * 2 / width, 0, 0, 0,
                0, 1 * -1 * 2 / height, 0, 0,
                0, 0, 1, 0,
                -1, 1, 0, 1]);
        }
        Begin(webgl: WebGLRenderingContext): void
        {
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);

            webgl.viewport(0, 0, webgl.drawingBufferWidth, webgl.drawingBufferHeight);
            //color 和 depth 一般都用不上
            webgl.clearColor(this.clearcolor.r, this.clearcolor.g, this.clearcolor.b, this.clearcolor.a);
            webgl.clearDepth(this.cleardepth);
            webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
        }
        End(webgl: WebGLRenderingContext): void
        {

        }
    }
    export class RenderTarget implements ITarget
    {
        //webgl: WebGLRenderingContext;
        constructor(webgl: WebGLRenderingContext, width: number, height: number)
        {
            //this.webgl = webgl;
            this.clearcolor = new BitMath.Color();
            this.clearcolor.g = 0.5;
            let pixelUnit = 1.0;
            //this.worldMatrix = new Float32Array(16);
            //翻转为左上角0，0
            this.worldMatrix = new Float32Array([
                1.0 * 2 / width * pixelUnit, 0, 0, 0,
                0, 1 * -1 * 2 / height * pixelUnit, 0, 0,
                0, 0, 1, 0,
                -1, 1, 0, 1]);
            this._width = width;
            this._height = height;
            let _buf = webgl.createFramebuffer();
            if (!_buf) throw "error";
            this.buf = _buf;


            this.texture = webgl.createTexture();
            webgl.bindTexture(webgl.TEXTURE_2D, this.texture);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST);
            //webgl.pixelStorei(webgl.UNPACK_FLIP_Y_WEBGL, 1)rt 不认这个
            webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, width, height, 0, webgl.RGBA, webgl.UNSIGNED_BYTE, null);



            //深度和蒙版可以不用
            //let rb = webgl.createRenderbuffer();
            //webgl.bindRenderbuffer(webgl.RENDERBUFFER, rb);
            //webgl.renderbufferStorage(webgl.RENDERBUFFER, webgl.DEPTH_COMPONENT16, width, height);

            webgl.bindFramebuffer(webgl.FRAMEBUFFER, _buf);
            webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D,
                this.texture, 0);
            // webgl.framebufferRenderbuffer(webgl.FRAMEBUFFER, webgl.DEPTH_ATTACHMENT,
            //     webgl.RENDERBUFFER, rb);
        }
        buf: WebGLFramebuffer;
        //tex: WebGLTexture;

        _width: number;
        _height: number;
        get width(): number
        {
            return this._width;
        }
        get height(): number
        {
            return this._height;
        }
        texture: WebGLTexture | null;
        worldMatrix: Float32Array;
        clearcolor: BitMath.Color;

        Begin(webgl: WebGLRenderingContext): void
        {
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, this.buf);

            webgl.viewport(0, 0, this._width, this._height);
            webgl.clearColor(this.clearcolor.r, this.clearcolor.g, this.clearcolor.b, this.clearcolor.a);
            //webgl.clearDepth(this.cleardepth);
            webgl.clear(webgl.COLOR_BUFFER_BIT);//|webgl.DEPTH_BUFFER_BIT);
        }
        End(webgl: WebGLRenderingContext): void
        {
            webgl.flush();
        }
    }

    export enum TextureFormat
    {
        RGB,
        RGBA,
        GRAY,
        RGBAF16,
        RGBAF32,
    }
    export class Texture
    {
        texture: WebGLTexture;
        webgl: WebGLRenderingContext;
        mipmap: boolean;
        linear: boolean;
        width: number;
        height: number;
        loaded: boolean;
        format: TextureFormat;
        constructor(webgl: WebGLRenderingContext, format: TextureFormat)
        {
            this.webgl = webgl;
            this.mipmap = false;
            this.linear = false;
            this.width = 0;
            this.height = 0;
            this.loaded = false;
            this.format = format;
            let _tex = webgl.createTexture();
            if (_tex == null)
                throw "error";
            this.texture = _tex;

        }

        loadimg(img: HTMLImageElement, mipmap: boolean, linear: boolean): void
        {
            this.mipmap = mipmap;

            this.width = img.width;
            this.height = img.height;
            this.loaded = true;
            this.webgl.pixelStorei(this.webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
            this.webgl.pixelStorei(this.webgl.UNPACK_FLIP_Y_WEBGL, 1);


            this.webgl.bindTexture(this.webgl.TEXTURE_2D, this.texture);
            var formatGL = this.webgl.RGBA;
            var type = this.webgl.UNSIGNED_BYTE;

            if (this.format == TextureFormat.RGB)
                formatGL = this.webgl.RGB;
            else if (this.format == TextureFormat.GRAY)
                formatGL = this.webgl.LUMINANCE;
            else if (this.format == TextureFormat.RGBAF16)
            {
                formatGL = this.webgl.RGBA;
                var ext = this.webgl.getExtension('OES_texture_half_float');
                if (ext == null) throw "not support oes";
                type = ext.HALF_FLOAT_OES;
            }

            this.webgl.texImage2D(this.webgl.TEXTURE_2D,
                0,
                formatGL,
                formatGL,
                //最后这个type，可以管格式
                type
                , img);

            if (mipmap)
            {
                //生成mipmap
                this.webgl.generateMipmap(this.webgl.TEXTURE_2D);
            }
            this.SetLinear(linear, linear);
        }
        texImageSub(pixels: Uint8Array, x: number, y: number, width: number, height: number)
        {
            let level = 0;

            this.webgl.pixelStorei(this.webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
            this.webgl.pixelStorei(this.webgl.UNPACK_FLIP_Y_WEBGL, 1);
            this.webgl.bindTexture(this.webgl.TEXTURE_2D, this.texture);
            var formatGL = this.webgl.RGBA;
            var type = this.webgl.UNSIGNED_BYTE;

            if (this.format == TextureFormat.RGB)
                formatGL = this.webgl.RGB;
            else if (this.format == TextureFormat.GRAY)
                formatGL = this.webgl.LUMINANCE;
            else if (this.format == TextureFormat.RGBAF16)
            {
                formatGL = this.webgl.RGBA;
                var ext = this.webgl.getExtension('OES_texture_half_float');
                if (ext == null) throw "not support oes";
                type = ext.HALF_FLOAT_OES;
            }
            else if (this.format == TextureFormat.RGBAF32)
            {
                formatGL = this.webgl.RGBA;
                var ext32 = this.webgl.getExtension('OES_texture_float');
                if (ext32 == null) throw "not support oes";
                type = this.webgl.FLOAT;// ext.HALF_FLOAT_OES;
            }
            this.webgl.texSubImage2D(this.webgl.TEXTURE_2D, level,
                x, y, width, height, formatGL, type, pixels);
        }
        loadimgFromData(data: ArrayBufferView | null, width: number, height: number, mipmap: boolean, linear: boolean): void
        {
            this.mipmap = mipmap;

            this.width = width;
            this.height = height;
            this.loaded = true;
            this.webgl.pixelStorei(this.webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
            this.webgl.pixelStorei(this.webgl.UNPACK_FLIP_Y_WEBGL, 1);


            this.webgl.bindTexture(this.webgl.TEXTURE_2D, this.texture);
            var formatGL = this.webgl.RGBA;
            var type = this.webgl.UNSIGNED_BYTE;

            if (this.format == TextureFormat.RGB)
                formatGL = this.webgl.RGB;
            else if (this.format == TextureFormat.GRAY)
                formatGL = this.webgl.LUMINANCE;
            else if (this.format == TextureFormat.RGBAF16)
            {
                formatGL = this.webgl.RGBA;
                var ext = this.webgl.getExtension('OES_texture_half_float');
                if (ext == null) throw "not support oes";
                type = ext.HALF_FLOAT_OES;
            }
            else if (this.format == TextureFormat.RGBAF32)
            {
                formatGL = this.webgl.RGBA;
                var ext32 = this.webgl.getExtension('OES_texture_float');
                if (ext32 == null) throw "not support oes";
                type = this.webgl.FLOAT;// ext.HALF_FLOAT_OES;
            }
            this.webgl.texImage2D(this.webgl.TEXTURE_2D,
                0,
                formatGL,
                width, height, 0,
                formatGL,
                //最后这个type，可以管格式
                type
                , data);

            if (mipmap)
            {
                //生成mipmap
                this.webgl.generateMipmap(this.webgl.TEXTURE_2D);
            }
            this.SetLinear(linear, linear);
        }
        public SetLinear(min: boolean, mag: boolean): void
        {
            this.webgl.bindTexture(this.webgl.TEXTURE_2D, this.texture);

            if (this.mipmap)
            {
                if (min)
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MIN_FILTER, this.webgl.LINEAR_MIPMAP_LINEAR);
                }
                else
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MIN_FILTER, this.webgl.NEAREST_MIPMAP_NEAREST);
                }
                if (mag)
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MAG_FILTER, this.webgl.LINEAR);
                }
                else
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MAG_FILTER, this.webgl.NEAREST);
                }
            }
            else
            {
                if (min)
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MIN_FILTER, this.webgl.LINEAR);
                }
                else
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MIN_FILTER, this.webgl.NEAREST);
                }
                if (mag)
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MAG_FILTER, this.webgl.LINEAR);
                }
                else
                {
                    this.webgl.texParameteri(this.webgl.TEXTURE_2D, this.webgl.TEXTURE_MAG_FILTER, this.webgl.NEAREST);
                }
            }
        }

    }
}